#include "stdint.h"
#include "syg_rtos_conf_internal.h"
#include "task.h"
#include "port.h"
#include "loadElf.h"

#if USE_LOADELF==1

#include "ff.h"

extern const void* sys_api_table[];

 APP_t* APP_load(const char* APP_file_path)
{
	FIL APP_file;							//APP file
	FRESULT file_ret=NULL;
	Elf32_Ehdr elf_hdr;				//elf head
	Elf32_Phdr program_head;	//program head
	Elf32_Shdr section_head;		//section head
	uint32_t read_size;
	APP_t* APP=NULL;				//APP to return
	
	APP=memMalloc(sizeof(APP_t));
	if(APP==NULL)
		goto err_return;
	memset(APP,0,sizeof(APP));
	
	file_ret=f_open(&APP_file,APP_file_path,FA_WRITE | FA_READ);
	if(file_ret!=FR_OK)
		goto err_return;
	
	/*read ELF head*/
	file_ret=f_read(&APP_file,&elf_hdr,sizeof(elf_hdr),&read_size);
	if (file_ret!=FR_OK||sizeof(elf_hdr)!=read_size||memcmp(elf_hdr.e_ident, ELF_MAGIC, 4) != 0)
		goto err_return;
	
	/*read program head*/
	file_ret=f_lseek(&APP_file,elf_hdr.e_phoff);
	if(file_ret!=FR_OK)
		goto err_return;
	file_ret=f_read(&APP_file,&program_head,elf_hdr.e_phentsize,&read_size);
	if (file_ret!=FR_OK||sizeof(program_head)!=read_size)
		goto err_return;
	
	/*read data section head*/
	file_ret=f_lseek(&APP_file,elf_hdr.e_shoff);
	if(file_ret!=FR_OK)
		goto err_return;
	for(uint32_t i=0;i<elf_hdr.e_shnum;i++)
	{
		file_ret=f_read(&APP_file,&section_head,elf_hdr.e_shentsize,&read_size);
		if (file_ret!=FR_OK||sizeof(section_head)!=read_size)
			goto err_return;
		 if ((section_head.sh_flags & SHF_ALLOC) && (section_head.sh_flags & SHF_WRITE))
			 break;
	}
	
	/*init APP*/
	APP->P_APP_mem_size=program_head.p_memsz+4;
	APP->P_APP_mem=memMalloc(APP->P_APP_mem_size);
	if(APP->P_APP_mem==NULL)
		goto err_return;
	APP->P_APP_load=APP->P_APP_mem+(4-(uint32_t)APP->P_APP_mem%4);
	file_ret=f_lseek(&APP_file,program_head.p_offset);
	if(file_ret!=FR_OK)
		goto err_return;
	file_ret=f_read(&APP_file,APP->P_APP_load,program_head.p_filesz,&read_size);	//load APP data to mem
	if(file_ret!=FR_OK||program_head.p_filesz!=read_size)
		goto err_return;
	APP->APP_entry=(void*)(APP->P_APP_load+(elf_hdr.e_entry-program_head.p_paddr));
	APP->P_APP_data=APP->P_APP_load+(section_head.sh_offset-program_head.p_offset);
	
	f_close(&APP_file);
	return APP;
	
err_return:
	if(APP->P_APP_mem!=NULL)
		memFree(APP->P_APP_mem);
	if(APP!=NULL)
		memFree(APP);
	f_close(&APP_file);
	return NULL;
}

int APP_run(APP_t * APP,void* APP_arg)
{
	if(APP==NULL)
		return NULL;
	int APP_ret;
	APP->temp_r9=get_r9();
	set_r9((uint32_t)APP->P_APP_data);
	APP_ret=APP->APP_entry(sys_api_table,APP,APP_arg);
	set_r9(APP->temp_r9);
	return APP_ret;
}

void APP_del(APP_t * APP)
{
	if(APP==NULL)
		return;
	if(APP->P_APP_mem!=NULL)
		memFree(APP->P_APP_mem);
	memFree(APP->P_APP_mem);
}

#endif